#include "stdafx.h"
#include "Terrain.h"
#include "Globals.h"


Terrain::Terrain(char *szFileHeightfileName, char **aszFileTextureName, int textureCount)
: Entity(), MapRows(VERTEX_ROWS - 1), MapColumns(VERTEX_COLS - 1)
{
	m_szFileHeightfeildName = szFileHeightfileName;
	m_aszFileTextureName = aszFileTextureName;
	m_iTexureCount = textureCount;
	m_HeightData = 0;
}

Vertex* Terrain::GenerateMapVertexPosition(int iNumVertexRows, int iNumVertexCols, int iNumCellRows, int iNumCellCols, float fDx, float fDz)
{
	m_fDx = fDx;
	m_fDz = fDz;

	//Setup size of terrain
	m_fWidth = iNumCellCols * m_fDx;
	m_fHeight = iNumCellRows * m_fDz;

	float fOffsetX = -m_fWidth / 2.0f;
	float fOffsetZ = -m_fHeight / 2.0f;

	///Calculate vertex position

	//genvertex
	long lVerteices = iNumVertexRows * iNumVertexCols;
	Vertex* data = new Vertex[lVerteices];

	int idx = 0;
	for (int i = 0; i<iNumVertexRows; i++)
	{
		for (int j = 0; j<iNumVertexCols; j++)
		{
			data[idx].position.x = j*fDx + fOffsetX;
			data[idx].position.z = i*fDz + fOffsetZ;
			//Check can set height level from data
			if (HEIGHT_LEVEL)
			{
				data[idx].position.y = m_HeightData->HeightData[idx];
			}
			else
			{
				data[idx].position.y = 0.0f;
			}

			data[idx].texcoord.x = (data[idx].position.x - fOffsetX) / m_fWidth * 5.0f;
			data[idx].texcoord.y = (data[idx].position.z - fOffsetZ) / m_fWidth * 5.0f;

			//printf("%f %f %f  ---- ", data[idx].position.x, data[idx].position.y, data[idx].position.z);

			idx++;
		}
		//printf("\n");
	}

	m_Models[0]->SetVertices(data, lVerteices);

	return data;
}

unsigned short* Terrain::GenerateMapVertexIndex(int iNumVertexRows, int iNumVertexCols, int iNumCellRows, int iNumCellCols, Vertex* aVertices)
{
	//Number of triangle create in map, one cell has two triangles
	long iTriagnles = iNumCellRows * iNumCellCols * 2;

	//Number of index, one triangle has three vertices make
	long lIndices = iTriagnles * 3;

	GLushort* data = new GLushort[lIndices];

	int idx = 0;
	for (int i = 0; i<iNumCellRows; ++i)
	{
		for (int j = 0; j<iNumCellRows; ++j)
		{
			//First triangle of cell; ABC
			data[idx++] = i*iNumVertexCols + j;		//A
			data[idx++] = i*iNumVertexCols + (j + 1);//B
			data[idx++] = (i + 1)*iNumVertexCols + j;//C
			//Compute sum normal vector for vertices of triangle ABC
			GenarateMapVertexNormal(aVertices[data[idx - 3]], aVertices[data[idx - 2]], aVertices[data[idx - 1]]);

			//Second triangle of cell; CBD
			data[idx++] = (i + 1)*iNumVertexCols + j;	//C
			data[idx++] = i*iNumVertexCols + (j + 1);	//B
			data[idx++] = (i + 1)*iNumVertexCols + (j + 1);//D
			//Compute sum normal vector for vertices of triangle CBD
			GenarateMapVertexNormal(aVertices[data[idx - 3]], aVertices[data[idx - 2]], aVertices[data[idx - 1]]);
		}
	}

	for (int i = 0; i<iNumVertexRows; i++)
	{
		for (int j = 0; j<iNumVertexCols; j++)
		{
			aVertices[j*iNumVertexCols + j].normal = (aVertices[j*iNumVertexCols + j].normal / 6).Normalize();
		}
	}

	m_Models[0]->SetIndices(data, lIndices);

	return data;
}

void Terrain::GenarateMapVertexNormal(Vertex &A, Vertex &B, Vertex &C)
{
	Vector3 vA = A.position;
	Vector3 vB = B.position;
	Vector3 vC = C.position;

	Vector3 AB = vB - vA;
	Vector3 AC = vB - vA;
	Vector3 BA = vA - vB;
	Vector3 BC = vC - vA;
	Vector3 CA = vA - vC;
	Vector3 CB = vB - vC;

	A.normal += AB.Cross(AC);
	B.normal += BA.Cross(BC);
	C.normal += CA.Cross(CB);
}

void Terrain::Init()
{
	m_NumModel = 1;
	//Load Texture Data
	m_Textures = new Texture*[1];
	m_Textures[0] = new Texture();
	m_Textures[0]->Load2DTextures(m_aszFileTextureName, m_iTexureCount);

	//Load Vertex Data
	int iNumVertexRows = VERTEX_ROWS;
	int iNumVertexCols = VERTEX_COLS;
	int iNumCellRows = iNumVertexRows - 1;
	int iNumCellCols = iNumVertexCols - 1;

	//Load Height Map data
	m_HeightData = new HeightField();
	m_HeightData->LoadHeightData(m_szFileHeightfeildName, iNumVertexRows, iNumVertexCols, 1.0f);


	m_Models = new Model*[1];
	m_Models[0] = new Model();
	Vertex* vertices = GenerateMapVertexPosition(iNumVertexRows, iNumVertexCols, iNumCellRows, iNumCellCols, 1.0f, 1.0f);
	unsigned short* indices = GenerateMapVertexIndex(iNumVertexRows, iNumVertexCols, iNumCellRows, iNumCellCols, vertices);
	Delete_Array(vertices);
	Delete_Array(indices);
	m_Models[0]->setTextureID(m_Textures[0]->m_IdTexture[0]);
}

void Terrain::update(float deltaTime)
{
	//TODO: Not do any things
}

void Terrain::draw()
{
	glUseProgram(m_Shader->program);

	m_Models[0]->draw(m_Shader);

	GLuint* iTextureID = m_Textures[0]->m_IdTexture;
	for (int i = 0; i < m_iTexureCount; i++)
	{
		if (m_Shader->samples[i] != -1)
		{
			glActiveTexture(GL_TEXTURE0 + i);
			glBindTexture(GL_TEXTURE_2D, iTextureID[i]);
			glUniform1i(m_Shader->samples[i], i);
		}
	}

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Models[0]->IND);
	
	int size;
	glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
	size /= sizeof(unsigned short);
	int index = m_Models[0]->getNumIndices();
	glDrawElements(GL_TRIANGLES, index, GL_UNSIGNED_SHORT, 0);

/*
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glBindTexture(GL_TEXTURE_2D, 0);*/
}

Terrain::~Terrain()
{
	//SAFE_DEL(mTexture);
	SAFE_DEL(m_HeightData);
}

bool Terrain::CanMove(int row, int col)
{
	return true;
}

float Terrain::GetHeight(int row, int col)
{
	//Domy code
	//It will get form function in HeightField class, it element after
	//HIHI
	return m_HeightData->HeightData[(row*MapColumns - 1) + col];
}

float Terrain::GetHeight(float x, float z)
{
	Vector4 campos = Vector4(x, 0, z, 0);
	campos = campos * this->m_Models[0]->getInvertWorld();
	x = campos.x;
	z = campos.z;
	//Transform terrain from local space to "cell" space
	float c = (x + 0.5f*m_fWidth) / m_fDx;
	float r = (z + 0.5f*m_fHeight) / m_fDz;

	//Get row and column here
	int col = (int)c;
	int row = (int)r;

	//Get grid height cell here
	float A = m_HeightData->getHeight(row, col);
	float B = m_HeightData->getHeight(row, col + 1);
	float C = m_HeightData->getHeight(row + 1, col);
	float D = m_HeightData->getHeight(row + 1, col + 1);

	float s = c - (float)col;
	float t = r - (float)row;

	if (t = 1.0f - s) //Uper triangle ABC
	{
		float uy = B - A;
		float vy = C - A;
		float height = A + s*uy + t*vy;
		Vector4 vheight = Vector4(0.0f, height, 0.0f, 0.0f);
		vheight = vheight * this->m_Models[0]->getWorld();
		return vheight.y+10;
	}
	else //lower triangle DCB
	{
		float uy = C - D;
		float vy = B - D;
		float height = D + (1.0f - s)*uy + (1.0f - t)*vy;
		Vector4 vheight = Vector4(0.0f, height, 0.0f, 0.0f);
		vheight = vheight * this->m_Models[0]->getWorld();
		return vheight.y+10;
	}
}